package Config::System::Section;

use Moose;
use List::Util qw(first);
use aliased 'Moose::Meta::Class';
use aliased 'Config::System::Property';

has name       => (is => 'ro', isa => 'Str'     , required => 1);
has files      => (is => 'ro', isa => 'ArrayRef', required => 1);
has properties => (is => 'ro', isa => 'HashRef' , required => 1, default => sub { {} });

# compile time -----------------------------------------------------------------

sub add_property {
    my ($self, $property_name, $descriptor) = @_;
    $self->properties->{$property_name} = Property->new
        (%$descriptor, name => $property_name, section => $self->name);
}

sub link {
    my ($self, $link) = @_;
    my $link_properties = $link->properties;
    my $self_properties = $self->properties;
    # TODO die if collisions, and avoidance mechanism
    my @linked;
    for my $name (keys %$link_properties) {
        $self_properties->{$name} = $link_properties->{$name};
        push @linked, $name;
    }
    return @linked;
}

# run time ---------------------------------------------------------------------

for my $method (
    qw(get set set_if_1st set_expression set_expression_if_1st)
) {
    __PACKAGE__->meta->add_method($method => sub {
        my ($self, $name, $value) = @_;
        my $property = $self->properties->{$name};
        die "Can't $method property [$name] in section [". $self->name. "] because no such property exists"
            unless $property;

        $self->read_property_from_files($property) unless
            $method eq 'set' or $method eq 'set_expression';

        my $result = eval { $property->$method($method eq 'get'? (): $value) };
        die "Can't [$method] on property [$name] from section [". $self->name. "] because: $@" if $@;
        return $result;
    });
}

sub read_property_from_files {
    my ($self, $property) = @_;
    return unless $property->NEVER_BEEN_SET;

    my $property_name = $property->name;
    my $section_name  = $property->section;
    my $first_file = first {
        my %parsed  = $_->parse;
        my $section = $parsed{ $section_name };
        ($section && exists $section->{$property_name})? 1: 0;
    } @{ $self->files };
    return unless $first_file;

    $property->set_expression
        ( { $first_file->parse }->{ $section_name }->{ $property_name } );
}

sub clone {
    my ($self, $files) = @_;
    local $_;
    return ref($self)->new(
        name       => $self->name,
        files      => $files,
        properties => {
            map
                { ($_ => $self->properties->{$_}->clone) }
                keys %{ $self->properties }
        },
    );
}

1;


